﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reactive.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using AIStudio.Wpf.DiagramApp.Views;
using AIStudio.Wpf.DiagramDesigner;
using AIStudio.Wpf.DiagramDesigner.Additionals;
using AIStudio.Wpf.DiagramDesigner.Additionals.Commands;
using AIStudio.Wpf.DiagramDesigner.ViewModels;
using AIStudio.Wpf.DiagramDesigner.ViewModels.BaseViewModel;
using AIStudio.Wpf.Mind.Models;
using ControlzEx.Theming;
using Dragablz;
using Fluent;
using Newtonsoft.Json;

namespace AIStudio.Wpf.DiagramApp.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        private IDiagramServiceProvider _service
        {
            get
            {
                return DiagramServicesProvider.Instance.Provider;
            }
        }
        private string _history = System.AppDomain.CurrentDomain.BaseDirectory + "history.json";

        public MainWindowViewModel()
        {
            ToolBoxViewModel = new ToolBoxViewModel(this);

            PageViewModels = new ObservableCollection<PageViewModel>();
            PageViewModels.Add(new PageViewModel("新建-1", "*", DiagramType.Normal));
            PageViewModel = PageViewModels.FirstOrDefault();

            StandardColor = GenerateStandardGradients();

            if (File.Exists(_history))
            {
                HistoryList = JsonConvert.DeserializeObject<ObservableCollection<string>>(File.ReadAllText(_history));
            }
            else
            {
                HistoryList = new ObservableCollection<string>();
            }
            _service.PropertyChanged += Provider_PropertyChanged;
        }

        #region 属性
        public ToolBoxViewModel ToolBoxViewModel
        {
            get; private set;
        }

        private ObservableCollection<PageViewModel> _pageViewModels;
        public ObservableCollection<PageViewModel> PageViewModels
        {
            get
            {
                return _pageViewModels;
            }
            set
            {
                SetProperty(ref _pageViewModels, value);
            }
        }

        private PageViewModel _pageViewModel;
        public PageViewModel PageViewModel
        {
            get
            {
                return _pageViewModel;
            }
            set
            {
                if (SetProperty(ref _pageViewModel, value))
                {

                }
            }
        }

        private Models.ColorType _colorType;
        public Models.ColorType ColorType
        {
            get
            {
                return _colorType;
            }
            set
            {
                SetProperty(ref _colorType, value);
            }
        }

        private bool _isOpenBackstage;

        public bool IsOpenBackstage
        {
            get
            {
                return _isOpenBackstage;
            }
            set
            {
                SetProperty(ref _isOpenBackstage, value);
            }
        }

        private ObservableCollection<string> _historyList;
        public ObservableCollection<string> HistoryList
        {
            get
            {
                return _historyList;
            }
            set
            {
                SetProperty(ref _historyList, value);
            }
        }

        public Color[] StandardColor
        {
            get; set;
        }

        public IDrawModeViewModel DrawModeViewModel
        {
            get
            {
                return _service.DrawModeViewModel;
            }
        }

        public IFontViewModel FontViewModel
        {
            get
            {
                return _service.FontViewModel;
            }
        }
        public IColorViewModel ColorViewModel
        {
            get
            {
                return _service.ColorViewModel;
            }
        }
        public IShapeViewModel ShapeViewModel
        {
            get
            {
                return _service.ShapeViewModel;
            }
        }
        public IAnimationViewModel AnimationViewModel
        {
            get
            {
                return _service.AnimationViewModel;
            }
        }
        public IQuickThemeViewModel QuickThemeViewModel
        {
            get
            {
                return _service.QuickThemeViewModel;
            }
        }
        public ILockObjectViewModel LockObjectViewModel
        {
            get
            {
                return _service.LockObjectViewModel;
            }
        }

        public SelectableDesignerItemViewModelBase SelectedItemViewModel
        {
            get
            {
                return _service.SelectedItemViewModel;
            }
        }

        public Color ThemeColor
        {
            get => ((SolidColorBrush)Application.Current.Resources["Fluent.Ribbon.Brushes.AccentBaseColorBrush"])?.Color ?? Colors.Pink;

            set
            {
                var solidColorBrush = new SolidColorBrush(value);
                solidColorBrush.Freeze();
                Application.Current.Resources["Fluent.Ribbon.Brushes.AccentBaseColorBrush"] = solidColorBrush;
            }
        }

        public string CurrentBaseColor
        {
            get => this.CurrentTheme.BaseColorScheme;

            set
            {
                if (value is null)
                {
                    return;
                }

                ThemeManager.Current.ChangeThemeBaseColor(Application.Current, value);
                RaisePropertyChanged(nameof(this.CurrentTheme));
            }
        }

        public Theme CurrentTheme
        {
            get => ThemeManager.Current.DetectTheme(Application.Current);

            set
            {
                if (value is null)
                {
                    return;
                }

                ThemeManager.Current.ChangeTheme(Application.Current, value);
                RaisePropertyChanged(nameof(this.CurrentBaseColor));
            }
        }
        #endregion

        public Func<PageViewModel> NewItemFactory
        {
            get
            {
                return
                    () => {
                        return new PageViewModel(NewNameHelper.GetNewName(PageViewModels.Select(p => p.Title), "新建-"), "*", DiagramType.Normal);
                    };
            }
        }

        #region 命令
        private ICommand _newCommand;
        public ICommand NewCommand
        {
            get
            {
                return this._newCommand ?? (this._newCommand = new DelegateCommand<string>(para => this.New_Executed(para)));
            }
        }

        private ICommand _newMindCommand;
        public ICommand NewMindCommand
        {
            get
            {
                return this._newMindCommand ?? (this._newMindCommand = new DelegateCommand<string>(para => this.NewMind_Executed(para)));
            }
        }

        private ICommand _openCommand;
        public ICommand OpenCommand
        {
            get
            {
                return this._openCommand ?? (this._openCommand = new DelegateCommand<string>(para => this.OpenExecuted(para)));
            }
        }

        private ICommand _saveCommand;
        public ICommand SaveCommand
        {
            get
            {
                return this._saveCommand ?? (this._saveCommand = new CanExecuteDelegateCommand(() => this.SaveExecuted(), () => this.Save_Enable()));
            }
        }

        private ICommand _saveAsCommand;
        public ICommand SaveAsCommand
        {
            get
            {
                return this._saveAsCommand ?? (this._saveAsCommand = new CanExecuteDelegateCommand(() => this.SaveAsExecuted(), () => this.Save_Enable()));
            }
        }

        private ICommand _pasteCommand;
        public ICommand PasteCommand
        {
            get
            {
                return this._pasteCommand ?? (this._pasteCommand = new CanExecuteDelegateCommand(() => this.PasteExecuted(), () => this.Paste_Enabled()));
            }
        }

        private ICommand _cutCommand;
        public ICommand CutCommand
        {
            get
            {
                return this._cutCommand ?? (this._cutCommand = new CanExecuteDelegateCommand(() => this.CutExecuted(), () => this.Cut_Enabled()));
            }
        }

        private ICommand _copyCommand;
        public ICommand CopyCommand
        {
            get
            {
                return this._copyCommand ?? (this._copyCommand = new CanExecuteDelegateCommand(() => this.CopyExecuted(), () => Copy_Enabled()));
            }
        }

        private ICommand _exitCommand;
        public ICommand ExitCommand
        {
            get
            {
                return this._exitCommand ?? (this._exitCommand = new CanExecuteDelegateCommand(() => this.ExitExecuted()));
            }
        }

        private ICommand _formatCommand;
        public ICommand FormatCommand
        {
            get
            {
                return this._formatCommand ?? (this._formatCommand = new CanExecuteDelegateCommand(() => this.FormatExecuted(), () => Format_Enabled()));
            }
        }

        private ICommand _deleteCommand;
        public ICommand DeleteCommand
        {
            get
            {
                return this._deleteCommand ?? (this._deleteCommand = new CanExecuteDelegateCommand(() => this.DeleteExecuted(), () => Delete_Enabled()));
            }
        }      

        private ICommand _lockCommand;
        public ICommand LockCommand
        {
            get
            {
                return this._lockCommand ?? (this._lockCommand = new DelegateCommand<object>(para => this.LockExecuted(para)));
            }
        }

        private ICommand _unlockCommand;
        public ICommand UnlockCommand
        {
            get
            {
                return this._unlockCommand ?? (this._unlockCommand = new DelegateCommand<object>(para => this.UnlockExecuted(para)));
            }
        }

        private ICommand _selectedColorCommand;
        public ICommand SelectedColorCommand
        {
            get
            {
                return this._selectedColorCommand ?? (this._selectedColorCommand = new DelegateCommand<object>(para => this.SelectedColorExecuted(para)));
            }
        }      

        private ICommand _aboutCommand;
        public ICommand AboutCommand
        {
            get
            {
                return this._aboutCommand ?? (this._aboutCommand = new DelegateCommand(() => this.AboutExecuted()));
            }
        }

        private ICommand _colorPickerCommand;
        public ICommand ColorPickerCommand
        {
            get
            {
                return this._colorPickerCommand ?? (this._colorPickerCommand = new DelegateCommand(() => this.ColorPickerExecuted()));
            }
        }

        private ICommand _screenshotCommand;
        public ICommand ScreenshotCommand
        {
            get
            {
                return this._screenshotCommand ?? (this._screenshotCommand = new DelegateCommand(() => this.ScreenshotExecuted()));
            }
        }     
        #endregion

        public ItemActionCallback ClosingTabItemHandler
        {
            get
            {
                return ClosingTabItemHandlerImpl;
            }
        }

        /// <summary>
        /// Callback to handle tab closing.
        /// </summary>        
        private void ClosingTabItemHandlerImpl(ItemActionCallbackArgs<TabablzControl> args)
        {
            //here's how you can cancel stuff:
            //args.Cancel(); 

            if (args.DragablzItem.DataContext is PageViewModel viewModel)
            {
                viewModel.Dispose();
            }
        }

        private void Provider_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(DrawModeViewModel)
                || e.PropertyName == nameof(FontViewModel)
                || e.PropertyName == nameof(ColorViewModel)
                || e.PropertyName == nameof(ShapeViewModel)
                || e.PropertyName == nameof(AnimationViewModel)
                || e.PropertyName == nameof(QuickThemeViewModel)
                || e.PropertyName == nameof(LockObjectViewModel)
                || e.PropertyName == nameof(SelectedItemViewModel))
            {
                RaisePropertyChanged(e.PropertyName);
            }

            if (PageViewModel == null || PageViewModel.DiagramViewModel == null) return;

            if (sender is IFontViewModel)
            {
                PageViewModel.DiagramViewModel.SetFont(sender as IFontViewModel, e.PropertyName, PageViewModel.DiagramViewModel.SelectedItems);
            }
            else if (sender is IColorViewModel)
            {
                PageViewModel.DiagramViewModel.SetColor(sender as IColorViewModel, e.PropertyName, PageViewModel.DiagramViewModel.SelectedItems);
            }
            else if (sender is IShapeViewModel)
            {
                PageViewModel.DiagramViewModel.SetSharp(sender as IShapeViewModel, e.PropertyName, PageViewModel.DiagramViewModel.SelectedItems);
            }
            else if (sender is IAnimationViewModel)
            {
                PageViewModel.DiagramViewModel.SetAnimation(sender as IAnimationViewModel, e.PropertyName, PageViewModel.DiagramViewModel.SelectedItems);
            }
            else if (sender is IQuickThemeViewModel)
            {
                PageViewModel.DiagramViewModel.SetQuickItem(sender as IQuickThemeViewModel, e.PropertyName, PageViewModel.DiagramViewModel.SelectedItems);
            }
            else if (sender is LockObject)
            {
                PageViewModel.DiagramViewModel.LockAction(sender as LockObject, e.PropertyName, PageViewModel.DiagramViewModel.SelectedItems);
            }
            else if (sender is SelectableDesignerItemViewModelBase designer)
            {
                PageViewModel.DiagramViewModel.SetPropertyValue(designer, e.PropertyName, PageViewModel.DiagramViewModel.SelectedItems);
            }
        }


        public bool KeyExecuted(KeyEventArgs e)
        {
            var para = e.KeyboardDevice.Modifiers == ModifierKeys.None ? e.Key.ToString() : e.KeyboardDevice.Modifiers.ToString() + "+" + e.Key.ToString();
            bool executed = true;
            switch (para)
            {
                case "Control+O": OpenExecuted(); break;
                case "Control+N": New_Executed(); break;
                case "Control+S": SaveExecuted(); break;
                default: executed = false; break;
            }

            return executed;
        }      

        private void OpenExecuted(string para = null)
        {
            string filename = string.Empty;

            if (string.IsNullOrEmpty(para))
            {
                Microsoft.Win32.OpenFileDialog openFile = new Microsoft.Win32.OpenFileDialog();
                openFile.Filter = "Designer Files (*.xml;*.json)|*.xml;*.json|All Files (*.*)|*.*";

                if (openFile.ShowDialog() == false)
                {
                    return;
                }

                filename = openFile.FileName;
            }
            else
            {
                filename = para;
            }

            var viewmodel = PageViewModels.FirstOrDefault(p => p.FileName == filename);
            if (viewmodel != null)
            {
                PageViewModel = viewmodel;
                MessageBox.Show("文档已经打开");
                return;
            }

            var diagram = PageViewModel.OpenFile(filename, Path.GetExtension(filename));
            PageViewModel flow;
            if (diagram.DiagramType == DiagramType.FlowChart)
            {
                flow = new FlowchartViewModel(filename, diagram);
            }
            else if (diagram.DiagramType == DiagramType.Logical)
            {
                flow = new LogicalViewModel(filename, diagram);
            }
            else if (diagram.DiagramType == DiagramType.SFC)
            {
                flow = new SFCViewModel(filename, diagram);
            }
            else if (diagram.DiagramType == DiagramType.Mind)
            {
                flow = new MindViewModel(filename, diagram);
            }
            else if (diagram.DiagramType == DiagramType.Drawing)
            {
                flow = new DrawingViewModel(filename, diagram);
            }
            else
            {
                flow = new PageViewModel(filename, diagram);
            }
            PageViewModels.Add(flow);
            PageViewModel = flow;

            if (string.IsNullOrEmpty(para))
            {
                SaveHistory(PageViewModel);
            }
            else
            {
                IsOpenBackstage = false;
            }

        }

        private void SaveExecuted()
        {
            if (PageViewModel == null) return;

            if (PageViewModel.SaveFile())
            {
                SaveHistory(PageViewModel);
            }
        }

        private void SaveAsExecuted()
        {
            if (PageViewModel == null) return;

            if (PageViewModel.SaveFile(true))
            {
                SaveHistory(PageViewModel);
            }
        }

        private void SaveHistory(PageViewModel diagramsViewModel)
        {
            HistoryList.Remove(PageViewModel.FileName);
            HistoryList.Insert(0, PageViewModel.FileName);
            File.WriteAllText(_history, JsonConvert.SerializeObject(HistoryList));
        }

        private bool Save_Enable()
        {
            return PageViewModel != null;
        }

        private void PasteExecuted()
        {
            PageViewModel?.DiagramViewModel?.PasteCommand.Execute(null);
        }

        private bool Paste_Enabled()
        {
            return Clipboard.ContainsData(DataFormats.Serializable);
        }

        private void CopyExecuted()
        {
            PageViewModel?.DiagramViewModel?.CopyCommand.Execute(null);
        }

        private bool Copy_Enabled()
        {
            return PageViewModel != null && PageViewModel.DiagramViewModel != null && PageViewModel.DiagramViewModel.SelectedItems.Count() > 0;
        }

        private void DeleteExecuted()
        {
            PageViewModel?.DiagramViewModel?.DeleteCommand.Execute(null);
        }

        private bool Delete_Enabled()
        {
            return PageViewModel != null && PageViewModel.DiagramViewModel != null && PageViewModel.DiagramViewModel.SelectedItems.Count() > 0;
        }

        private void CutExecuted()
        {
            PageViewModel?.DiagramViewModel?.CutCommand.Execute(null);
        }

        private bool Cut_Enabled()
        {
            return PageViewModel != null && PageViewModel.DiagramViewModel != null && PageViewModel.DiagramViewModel.SelectedItems.Count() > 0;
        }

        private void FormatExecuted()
        {
            _service.DrawModeViewModel.CursorMode = CursorMode.Format;
        }

        private bool Format_Enabled()
        {
            return PageViewModel != null && PageViewModel.DiagramViewModel != null && PageViewModel.DiagramViewModel.SelectedItems.Count() == 1;
        }

        private void New_Executed(string type = "Normal")
        {
            IsOpenBackstage = false;
            if (type == DiagramType.FlowChart.ToString())
            {
                PageViewModel = new FlowchartViewModel(NewNameHelper.GetNewName(PageViewModels.Select(p => p.Title), "新建-"), "*", (DiagramType)Enum.Parse(typeof(DiagramType), type));
            }
            else if (type == DiagramType.Logical.ToString())
            {
                PageViewModel = new LogicalViewModel(NewNameHelper.GetNewName(PageViewModels.Select(p => p.Title), "新建-"), "*", (DiagramType)Enum.Parse(typeof(DiagramType), type));
            }
            else if (type == DiagramType.SFC.ToString())
            {
                PageViewModel = new SFCViewModel(NewNameHelper.GetNewName(PageViewModels.Select(p => p.Title), "新建-"), "*", (DiagramType)Enum.Parse(typeof(DiagramType), type));
            }
            else if (type == DiagramType.Drawing.ToString())
            {
                PageViewModel = new DrawingViewModel(NewNameHelper.GetNewName(PageViewModels.Select(p => p.Title), "新建-"), "*", (DiagramType)Enum.Parse(typeof(DiagramType), type));
            }
            else
            {
                PageViewModel = new PageViewModel(NewNameHelper.GetNewName(PageViewModels.Select(p => p.Title), "新建-"), "*", (DiagramType)Enum.Parse(typeof(DiagramType), type));
            }

            PageViewModels.Add(PageViewModel);
        }

        private void NewMind_Executed(string mindtype = "Mind")
        {
            IsOpenBackstage = false;

            PageViewModel = new MindViewModel(NewNameHelper.GetNewName(PageViewModels.Select(p => p.Title), "新建-"), "*", DiagramType.Mind, mindtype.ToEnum<MindType>());


            PageViewModels.Add(PageViewModel);
        }

        private void ExitExecuted()
        {
            
        }     

        private void LockExecuted(object para)
        {
            LockObjectViewModel.LockObject[0].IsChecked = true;
        }

        private void UnlockExecuted(object para)
        {
            LockObjectViewModel.LockObject.ForEach(p => p.IsChecked = false);
        }

        private void SelectedColorExecuted(object para)
        {
            if (para == null) return;

            switch (ColorType)
            {
                case Models.ColorType.Text: PageViewModel?.DiagramViewModel?.SetFont(new FontViewModel() { FontColor = (Color)para }, "FontColor", PageViewModel.DiagramViewModel.SelectedItems); break;
                case Models.ColorType.Fill: PageViewModel?.DiagramViewModel?.SetColor(new ColorViewModel() { FillColor = new ColorObject() { Color = (Color)para } }, "FillColor", PageViewModel.DiagramViewModel.SelectedItems); break;
                case Models.ColorType.Line: PageViewModel?.DiagramViewModel?.SetColor(new ColorViewModel() { LineColor = new ColorObject() { Color = (Color)para } }, "LineColor", PageViewModel.DiagramViewModel.SelectedItems); break;
            }
        }

        private void AboutExecuted()
        {
            AboutWindow aboutWindow = new AboutWindow();
            aboutWindow.ShowDialog();
        }

        private void ScreenshotExecuted()
        {
            AIStudio.Wpf.ComeCapture.MainWindow window = new AIStudio.Wpf.ComeCapture.MainWindow();
            window.Show();
        }

        private void ColorPickerExecuted()
        {
            AIStudio.Wpf.ColorPicker.MainWindow window = new AIStudio.Wpf.ColorPicker.MainWindow();
            window.Show();
        }

        #region 方法
        private Color[] GenerateStandardGradients()
        {
            var count = ColorGallery.StandardThemeColors.Length;
            List<Color> result = new List<Color>();
            for (var i = 0; i < count; i++)
            {
                var colors = GetGradient(ColorGallery.StandardThemeColors[i], 10);
                for (var j = 9; j >= 0; j--)
                {
                    result.Add(colors[j]);
                }
            }
            {
                var colors = GetGradient(Colors.Black, 10);
                for (var j = 9; j >= 0; j--)
                {
                    result.Add(colors[j]);
                }
            }
            return result.ToArray();
        }

        #endregion

        #region Gradient Generation

        /// <summary>
        /// Returns brightness of the given color from 0..1
        /// </summary>
        /// <param name="color">Color</param>
        /// <returns>Brightness of the given color from 0..1</returns>
        private static double GetBrightness(Color color)
        {
            var summ = (double)color.R + color.G + color.B;
            return summ / (255.0 * 3.0);
        }

        // Makes the given color lighter
        private static Color Lighter(Color color, double power)
        {
            var totalAvailability = (255.0 * 3.0) - color.R + color.G + color.B;
            double redAvailability;
            double greenAvailability;
            double blueAvailability;
            double needToBeAdded;

            if (color.R + color.G + color.B == 0)
            {
                redAvailability = 1.0 / 3.0;
                greenAvailability = 1.0 / 3.0;
                blueAvailability = 1.0 / 3.0;
                needToBeAdded = power * 255.0 * 3.0;
            }
            else
            {
                redAvailability = (255.0 - color.R) / totalAvailability;
                greenAvailability = (255.0 - color.G) / totalAvailability;
                blueAvailability = (255.0 - color.B) / totalAvailability;
                needToBeAdded = ((double)color.R + color.G + color.B) * (power - 1);
            }

            var result = Color.FromRgb(
                (byte)(color.R + (byte)(redAvailability * needToBeAdded)),
                (byte)(color.G + (byte)(greenAvailability * needToBeAdded)),
                (byte)(color.B + (byte)(blueAvailability * needToBeAdded)));

            return result;
        }

        // Makes the given color darker
        private static Color Darker(Color color, double power)
        {
            var totalAvailability = (double)color.R + color.G + color.B;
            var redAvailability = color.R / totalAvailability;
            var greenAvailability = color.G / totalAvailability;
            var blueAvailability = color.B / totalAvailability;

            var needToBeAdded = (double)color.R + color.G + color.B;
            needToBeAdded = needToBeAdded - (needToBeAdded * power);

            var result = Color.FromRgb(
                (byte)(color.R - (byte)(redAvailability * needToBeAdded)),
                (byte)(color.G - (byte)(greenAvailability * needToBeAdded)),
                (byte)(color.B - (byte)(blueAvailability * needToBeAdded)));

            return result;
        }

        // Makes a new color from the given with new brightness
        private static Color Rebright(Color color, double newBrightness)
        {
            var currentBrightness = GetBrightness(color);
            var power = DoubleUtil.AreClose(currentBrightness, 0.0) == false
                ? newBrightness / currentBrightness
                : 1.0 + newBrightness;

            // TODO: round power to make nice numbers
            // ...

            if (power > 1.0)
            {
                return Lighter(color, power);
            }

            return Darker(color, power);
        }

        /// <summary>
        /// Makes gradient colors from lighter to darker
        /// </summary>
        /// <param name="color">Base color</param>
        /// <param name="count">Count of items in the gradient</param>
        /// <returns>Colors from lighter to darker</returns>
        public static Color[] GetGradient(Color color, int count)
        {
            const double lowBrightness = 0.15;
            const double highBrightness = 0.85;
            var result = new Color[count];

            for (var i = 0; i < count; i++)
            {
                var brightness = lowBrightness + (i * (highBrightness - lowBrightness) / count);
                result[count - i - 1] = Rebright(color, brightness);
            }

            return result;
        }

        #endregion
    }
}
